/**
 * 
 */
package PagedFile;

import java.nio.channels.FileChannel;

/**
 * @author Neil
 *
 */
public class PF_FileHandle implements PF_FileHandle_Interface {

	public FileChannel fc;
	public String filename;
	public PF_FileHead fhead;
	BF_Manager bfm;
	
	int openCnt;
	
	public PF_FileHandle(String filename, FileChannel fc, PF_FileHead fhead, BF_Manager bfm) {
		this.fc = fc;
		this.filename = filename;
		this.fhead = fhead;
		this.bfm = bfm;
		openCnt = 1;
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#AllocatePage()
	 */
	@Override
	public Buffer AllocatePage() {
		for (int i=1; i<=fhead.lastPageNum; ++i)
			if (!fhead.bitTest(i)) {
				fhead.bitSet(i);
				fhead.nAllocatedPages++;
				return bfm.pin(new Block(filename, i), fc);
			}
		fhead.lastPageNum++;
		fhead.nAllocatedPages++;
		fhead.bitSet(fhead.lastPageNum);
		Buffer ret = bfm.pin(new Block(filename, fhead.lastPageNum), fc);
		ret.read = true;
/*		byte[] zeros = new byte[BF_Manager.PF_BUFFER_SIZE];
		ret.mark(this.hashCode());
		ret.contents.position(0);
		ret.contents.put(zeros);
*/
		return ret;
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#DisposePage(int)
	 */
	@Override
	public void DisposePage(int pageNum) {
		if (pageNum<1 || pageNum>fhead.lastPageNum) 
			throw new PF_Abort_Exception("Page Not Allocated");
		Buffer pageBuffer = bfm.find(new Block(filename, pageNum));
		while (pageBuffer.isPinned())
			bfm.unpin(pageBuffer);
		pageBuffer.mark(-1);
		fhead.bitUnSet(pageNum);
		if (pageNum == fhead.lastPageNum)
			while ( !fhead.bitTest(--fhead.lastPageNum));
		--fhead.nAllocatedPages;
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#ForcePages(int)
	 */
	@Override
	public void ForcePages(int pageNum) {
		Buffer pageBuffer = bfm.find(new Block(this.filename, pageNum));
		if (pageBuffer == null)
			throw new PF_Abort_Exception("Page Unpinned");
		pageBuffer.write();
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#GetFirstPage()
	 */
	@Override
	public Buffer GetFirstPage() {
		return GetNextPage(0);
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#GetLastPage()
	 */
	@Override
	public Buffer GetLastPage() throws PF_Abort_Exception {
		return GetThisPage(fhead.lastPageNum);
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#GetNextPage(int)
	 */
	@Override
	public Buffer GetNextPage(int current_pn) throws PF_Abort_Exception {
		for (int i=current_pn+1; i<= fhead.lastPageNum; ++i)
			if (fhead.bitTest(i))
				return GetThisPage(i);
		return null;
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#GetPrevPage(int)
	 */
	@Override
	public Buffer GetPrevPage(int current_pn) throws PF_Abort_Exception {
		for (int i=current_pn-1; i>0; --i)
			if (fhead.bitTest(i))
				return GetThisPage(i);
		return null;
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#GetThisPage(int)
	 */
	@Override
	public Buffer GetThisPage(int pageNum) {
		if (pageNum<1 || pageNum>fhead.lastPageNum || !PF_FileHead.bop.bitTest(fhead.bitmap, pageNum))
			throw new PF_Abort_Exception("Page Not Allocated");
		
		Buffer pageBuffer = bfm.pin(new Block(this.filename, pageNum), fc);
		pageBuffer.read();
		
		return pageBuffer;
	}

	/* (non-Javadoc)
	 * @see PagedFile.PF_FileHandle_Interface#UnpinPage(int)
	 */
	@Override
	public void UnpinPage(int pageNum) {
		Buffer pageBuffer = bfm.find(new Block(this.filename, pageNum));
		if (pageBuffer == null)
			throw new PF_Abort_Exception("Page Unpinned");
		bfm.unpin(pageBuffer);
	}

	@Override
	public void ForcePages() {
		bfm.flushAll(fc);
	}

	public boolean isAllocated(int pageNum) {
		return fhead.bitTest(pageNum);
	}
}
